package com.gmlive.common.network.domain;

import android.text.TextUtils;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.net.BindException;
import java.net.ConnectException;
import java.net.HttpRetryException;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
import java.net.PortUnreachableException;
import java.net.ProtocolException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.concurrent.atomic.AtomicLong;

import okhttp3.Call;
import okhttp3.EventListener;
import okhttp3.Response;

public class OkHttpEventListenerFactory implements EventListener.Factory {
    private static final int UNKNOWN_EXCEPTION = -1;
    private static final int REQ_CANCEL_EXCEPTION = 1001;
    private static final int SOCKET_EXCEPTION = 1002;
    private static final int SOCKET_TIMEOUT_EXCEPTION = 1003;
    private static final int UNKNOWN_HOST_EXCEPTION = 1004;
    private static final int PROTOCOL_EXCEPTION = 1005;
    private static final int PORT_UNREACHABLE_EXCEPTION = 1006;
    private static final int NOROUTE_TO_HOST_EXCEPTION = 1007;
    private static final int MALFORMED_URL_EXCEPTION = 1008;
    private static final int HTTP_RETRY_EXCEPTION = 1009;
    private static final int CONNECT_EXCEPTION = 1010;
    private static final int BIND_EXCEPTION = 1011;
    private static final int INVALID_RESPONSE_EXCEPTION = 1012;
    private static final int PERMISSION_DENIED_EXCEPTION = 1013;

    private static final long UNIT_NANO = 1000000L;

    private final AtomicLong mCallId = new AtomicLong(0);

    @NotNull
    @Override
    public EventListener create(Call call) {
        return new OkHttpEventListener(call.request().url().host(), mCallId.getAndIncrement());
    }

    private static class OkHttpEventListener extends EventListener {
        private final String mHost;
        private final long mCallId;

        private long mCallStart;
        private int mRespCode;

        OkHttpEventListener(String host, long callId) {
            mHost = host;
            mCallId = callId;
        }

        @Override
        public void callStart(Call call) {
            super.callStart(call);
            mCallStart = System.nanoTime();
        }

        @Override
        public void responseHeadersEnd(Call call, Response response) {
            super.responseHeadersEnd(call, response);
            mRespCode = response.code();
        }

        @Override
        public void callEnd(Call call) {
            super.callEnd(call);
            long callEnd = System.nanoTime();
            long spend = (callEnd - mCallStart) / UNIT_NANO;

            DomainManager.getInstance().reportQuality(mHost, spend, mRespCode);
        }

        @Override
        public void callFailed(Call call, IOException ioe) {
            super.callFailed(call, ioe);
            long callEnd = System.nanoTime();
            long spend = (callEnd - mCallStart) / UNIT_NANO;
            mRespCode = getErrorCoder(ioe);

            DomainManager.getInstance().reportQuality(mHost, spend, mRespCode);
        }
    }

    private static int getErrorCoder(IOException ioe) {
        if (ioe instanceof ConnectException) {
            return CONNECT_EXCEPTION;
        } else if (ioe instanceof SocketTimeoutException) {
            return SOCKET_TIMEOUT_EXCEPTION;
        } else if (ioe instanceof UnknownHostException) {
            return UNKNOWN_HOST_EXCEPTION;
        } else if (ioe instanceof ProtocolException) {
            return PROTOCOL_EXCEPTION;
        } else if (ioe instanceof PortUnreachableException) {
            return PORT_UNREACHABLE_EXCEPTION;
        } else if (ioe instanceof NoRouteToHostException) {
            return NOROUTE_TO_HOST_EXCEPTION;
        } else if (ioe instanceof MalformedURLException) {
            return MALFORMED_URL_EXCEPTION;
        } else if (ioe instanceof HttpRetryException) {
            return HTTP_RETRY_EXCEPTION;
        } else if (ioe instanceof BindException) {
            return BIND_EXCEPTION;
        } else if (ioe instanceof SocketException) {
            return SOCKET_EXCEPTION;
        } else if (ioe != null) {
            String errMsg = ioe.getMessage();
            if (!TextUtils.isEmpty(errMsg) && errMsg != null) {
                if (errMsg.contains("Canceled")) {
                    return REQ_CANCEL_EXCEPTION;
                } else if (errMsg.contains("Invalid Response")) {
                    return INVALID_RESPONSE_EXCEPTION;
                } else if (errMsg.contains("Permission Denied Response")) {
                    return PERMISSION_DENIED_EXCEPTION;
                }
            }

            return UNKNOWN_EXCEPTION;
        } else {
            return UNKNOWN_EXCEPTION;
        }
    }
}
